package avicit.bdp.dgs.qa.service.algo.raw.data.pcm.checker;

import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmParamInfo;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmStreamInfo;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmTime;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.utils.DataConvertUtils;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.utils.DataFileUtils;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.utils.FrameUtils;
import avicit.platform6.core.exception.BusinessException;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2023-11-01
 * @类说明：PCMDataReader
 * @修改记录：
 * @注意事项：
 * @主要功能：pcm文件reader
 */
public class PCMDataReader {
    private static final Logger logger = LoggerFactory.getLogger(PCMDataReader.class);

    private PCMConfigReader config;

    private DataFileUtils dataFileUtils;

    private FrameUtils frameUtils;

    public PCMDataReader(PCMConfigReader config) throws IOException {
        this.config = config;
    }

    public void readDataFrame(InputStream dataStream, int streamIndex, String groupParams, Map<String, Object> outputParamsMap) throws IOException {
        this.dataFileUtils = new DataFileUtils(dataStream);
        this.frameUtils = new FrameUtils(config.getPcmHeadInfo());

        double frame_step_time = 1000.0 / config.getPcmHeadInfo().getStreamInfoList().get(streamIndex).getMajor_frame_rate();
        byte[] first_frame_bytes = frameUtils.findFirstFrame(this.dataFileUtils);
        long count = 0;
        long start = System.currentTimeMillis();

        // 计算需要计算的参数
        String[] outputParams = groupParams.split(",");
        List<PcmParamInfo> pcmParamInfoList = new ArrayList<>();

        // 规则2.校验读取参数个数
        List<PcmStreamInfo> streamInfoList = config.getPcmHeadInfo().getStreamInfoList();
        int parameterNumber = 0;
        for (PcmStreamInfo pcmStreamInfo : streamInfoList) {
            parameterNumber += pcmStreamInfo.getParameter_number();
        }

        List<PcmParamInfo> paramInfoList = streamInfoList.get(streamInfoList.size() - 1).getParams();
        int lastParamSerialNo = paramInfoList.get(paramInfoList.size() - 1).getSerial_no();
        if (parameterNumber != lastParamSerialNo) {
            String errorInfo = String.format("stream[%s]参数数量与读取参数个数不一致，参数数量=%s,读取参数个数=%s", 
                    streamIndex, parameterNumber, lastParamSerialNo);
            throw new BusinessException(errorInfo);
        }

        for (PcmParamInfo paramInfo : streamInfoList.get(streamIndex).getParams()) {
            paramInfo.setName(paramInfo.getName().trim());
            // 兼容测试，如果输入为空，则默认加入所有参数
            if (StrUtil.isEmpty(groupParams)) {
                if (StrUtil.isNotBlank(paramInfo.getName())) {
                    pcmParamInfoList.add(paramInfo);
                }
            } else {
                // 只计算需要的参数
                if (ArrayUtil.contains(outputParams, paramInfo.getName())) {
                    pcmParamInfoList.add(paramInfo);
                }
            }
        }

        if (CollectionUtils.isEmpty(pcmParamInfoList)) {
            throw new BusinessException(String.format("stream[%s]计算参数为空", streamIndex));
        }

        // 计算最大的采样率
        int maxRate = 0;
        for (PcmParamInfo paramInfo : pcmParamInfoList) {
            if (paramInfo.getRate() > maxRate) {
                maxRate = paramInfo.getRate();
            }
        }

        // 处理首帧
        String[][] firstFrameArr = new String[maxRate][pcmParamInfoList.size() + 1];
        for (int i = 0; i < pcmParamInfoList.size(); i++) {
            PcmParamInfo paramInfo = pcmParamInfoList.get(i);
            Pair<Double[], PcmTime[]> result = readRealDataFrame(first_frame_bytes, paramInfo, count, maxRate, frame_step_time, streamIndex);
            Double[] values = result.getKey();
            PcmTime[] times = result.getValue();
            for (int j = 0; j < values.length; j++) {
                firstFrameArr[j][0] = times[j].toString();
                firstFrameArr[j][i + 1] = String.valueOf(values[j]);
            }
        }

        // 处理正常帧
        count++;
        try {
            // 处理正常帧
            byte[] frame_bytes = new byte[
                    config.getPcmHeadInfo().getStreamInfoList().get(streamIndex)
                            .getFrame_length() * config.getPcmHeadInfo().getStreamInfoList().get(streamIndex)
                            .getSubframe_deepth() * (config.getPcmHeadInfo().getStreamInfoList()
                            .get(streamIndex).getWord_length() / 8)];

            while (true) {
                if (count % 100 == 0) {
                    logger.debug("处理帧数：" + count + ", 耗时：" + (System.currentTimeMillis() - start) / 1000 + " 秒");
                    start = System.currentTimeMillis();
                }

                int len = this.dataFileUtils.readBytes(frame_bytes);
                if (len == -1) {
                    break;
                }

                boolean check = frameUtils.checkSyncWord(new byte[]{frame_bytes[0], frame_bytes[1], frame_bytes[2], frame_bytes[3]});
                if (!check) {
                    throw new BusinessException(String.format("stream[%d],checkSyncWord错误", streamIndex));
                }

                // 取ID字确定为0
                if (DataConvertUtils.byte2short(new byte[]{frame_bytes[4], frame_bytes[5]}) != 0) {
                    throw new BusinessException(String.format("stream[%d],byte2short错误", streamIndex));
                }

                count++;
            }
        } catch (FileNotFoundException e) {
            logger.error("未找到校验文件", e);
            throw new BusinessException("校验正常帧失败，校验失败");
        }

        logger.info("stream[{}}]解析的通道参数量：{} \n", streamIndex, pcmParamInfoList.size());
        logger.info("stream[{}}]数据总字节数为：{},总帧数：{} \n", streamIndex, this.dataFileUtils.count, count);
        this.dataFileUtils.close();
    }

    public Pair<Double[], PcmTime[]> readRealDataFrame(byte[] frame_bytes, PcmParamInfo paramInfo, long num, int rate,
                                                       double frame_step_time, int streamIndex) {
        double[] param_values = null;
        PcmTime frame_time = null;
        Double[] out_values = new Double[rate];

        if (num == 0) {
            // 处理首帧数据
            frame_time = frameUtils.getFirstFrameDate(frame_bytes);
            param_values = frameUtils.readFirstFrameParamData(frame_bytes, paramInfo);
        } else {
            // 处理正常帧数据
            frame_time = frameUtils.getFrameDate(frame_bytes);
            param_values = frameUtils.readParamData(frame_bytes, paramInfo);
        }

        if (frame_time == null || param_values == null) {
            logger.error("stream[{}]读取帧{}数据失败，参数名为{}", streamIndex, num, paramInfo.getName());
            throw new BusinessException(String.format("stream[%d]读取帧%d数据失败，参数名为%s", streamIndex, num, paramInfo.getName()));
        }

        PcmTime[] param_times = new PcmTime[rate];
        double time_step = frame_step_time / rate;
        int value_step = Math.abs(paramInfo.rate - rate) / paramInfo.rate;

        // 按采样率补充数据
        if (param_values.length == rate) {
            // 相同不变
            out_values = Convert.toDoubleArray(param_values);
            for (int i = 0; i < rate; i++) {
                param_times[i] = new PcmTime(frame_time.hour, frame_time.minute, frame_time.second, frame_time.ms);
                param_times[i].addMS(time_step * i);
            }
        } else if (param_values.length > rate) {
            // 实际采样率大于输入的采样率， 自动舍弃数据
            for (int i = 0; i < rate; i++) {
                out_values[i] = param_values[i * value_step];
                param_times[i] = new PcmTime(frame_time.hour, frame_time.minute, frame_time.second, frame_time.ms);
                param_times[i].addMS(time_step * i);
            }
        } else {
            // 实际采样率小于输入的采样率， 自动补充数据为null
            for (int i = 0; i < param_values.length; i++) {
                int temp = i * (value_step + 1);
                for (int j = 0; j < value_step + 1; j++) {
                    if (j == 0) {
                        out_values[j + temp] = param_values[i];
                    } else {
                        out_values[j + temp] = null;
                    }
                    param_times[j + temp] = new PcmTime(frame_time.hour, frame_time.minute, frame_time.second, frame_time.ms);
                    param_times[j + temp].addMS((j + temp) * time_step);
                }
            }
        }

        return new Pair<Double[], PcmTime[]>(out_values, param_times);
    }
}
